package org.apache.dolphinscheduler.api.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.api.client.util.Lists;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.service.BillService;
import org.apache.dolphinscheduler.api.service.UserGroupService;
import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.ExcelUtil;
import org.apache.dolphinscheduler.api.utils.PageInfo;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.api.vo.BillExportVo;
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.common.utils.DateUtils;
import org.apache.dolphinscheduler.dao.entity.Bill;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.BillMapper;
import org.apache.dolphinscheduler.dao.mapper.UserMapper;
import org.apache.dolphinscheduler.data.quality.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 收支明细表 服务实现类
 * </p>
 *
 * @author denggh
 * @since 2023-08-14
 */
@Service
public class BillServiceImpl extends BaseServiceImpl implements BillService {

    @Autowired
    private BillMapper billMapper;
    @Autowired
    private UsersService userService;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserGroupService userGroupService;

    @Override
    public Result selectPageBillList(User loginUser, Bill bill, Integer pageSize, Integer pageNo, String startDate,
                                     String endDate) {
        Result result = new Result();
        PageInfo<Bill> pageInfo = new PageInfo<>(pageNo, pageSize);

        Page<Bill> page = new Page<>(pageNo, pageSize);

        IPage<Bill> billIPage = null;
        LambdaQueryWrapper<Bill> lqw = new LambdaQueryWrapper<>();

        List<Integer> userIdList = Lists.newArrayList();
        // 如果不是管理员角色 只能查询当前用户收支明细
        // 如果用户是用户组管理员 可以查询用户组下所有用户的数据
        if (!isAdmin(loginUser)) {
            userIdList.add(loginUser.getId());
            List<Integer> userIds = userGroupService.queryAllUserByGroupAdmin(loginUser);
            if(CollectionUtils.isNotEmpty(userIds)){
                userIdList.addAll(userIds);
            }
        }

        // 交易账户
        if (Objects.nonNull(bill.getUserId())) {
            lqw.eq(Bill::getUserId, bill.getUserId());
        } else {
            if (CollectionUtils.isNotEmpty(userIdList)) {
                lqw.in(Bill::getUserId, userIdList);
            }
        }

        // 收支类型
        if (Objects.nonNull(bill.getBillType())) {
            lqw.eq(Bill::getBillType, bill.getBillType());
        }
        // 交易类型
        if (Objects.nonNull(bill.getTradeType())) {
            lqw.eq(Bill::getTradeType, bill.getTradeType());
        }
        // 开始时间
        if (StringUtils.isNotBlank(startDate)) {
            // LocalDate ld = LocalDate.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            // Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            // Date start = Date.from(instant);
            // lqw.ge(Bill::getCreateTime, DateUtils.format(DateUtils.getStartOfDay(start),
            // Constants.YYYY_MM_DD_HH_MM_SS));
            lqw.ge(Bill::getCreateTime, startDate);
        }
        // 结束时间
        if (StringUtils.isNotBlank(endDate)) {
            // LocalDate ld = LocalDate.parse(endDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            // Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            // Date end = Date.from(instant);
            // lqw.lt(Bill::getCreateTime, DateUtils.format(DateUtils.getEndOfDay(end), Constants.YYYY_MM_DD_HH_MM_SS));
            lqw.lt(Bill::getCreateTime, endDate);
        }
        lqw.orderByDesc(Bill::getCreateTime);
        billIPage = billMapper.selectPage(page, lqw);

        List<Bill> billList = billIPage.getRecords();
        for (Bill bill1 : billList) {
            bill1.setBillNumber(bill1.getBillNumber().setScale(2, RoundingMode.HALF_UP));
            if (Objects.nonNull(bill1.getUserId())) {
                User user = userService.queryUser(bill1.getUserId());
                bill1.setUserName(Objects.nonNull(user) ? user.getUserName() : null);
            }
        }
        pageInfo.setTotal((int) billIPage.getTotal());
        pageInfo.setTotalList(billList);
        result.setData(pageInfo);
        putMsg(result, Status.SUCCESS);
        return result;
    }

    @Override
    public Result selectBillTotal(User loginUser) {
        Result result = new Result();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("incomeTotal", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
        resultMap.put("expensesTotal", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
        Integer userId = null;
        // 如果不是管理员角色 只能查询当前用户收支明细
        if (!isAdmin(loginUser)) {
            userId = loginUser.getId();
        }
        // 当前账户收入列表
        List<Bill> incomeBills = billMapper.selectBillList(userId, 1, null, null);

        if (CollectionUtils.isNotEmpty(incomeBills)) {
            BigDecimal incomeTotal =
                    incomeBills.stream().map(Bill::getBillNumber).reduce(BigDecimal.ZERO, BigDecimal::add);
            resultMap.put("incomeTotal", String.valueOf(incomeTotal.setScale(2, RoundingMode.HALF_UP)));
        }
        // 查询上个月收入
        BigDecimal lastMonthTotalIncome = getMonthTotal(loginUser, false, true);
        // 查询本月收入
        BigDecimal curMonthTotalIncome = getMonthTotal(loginUser, true, true);
        // 计算环比值
        String incomeRatio = d2dRatio(curMonthTotalIncome, lastMonthTotalIncome);
        resultMap.put("incomeRatio", incomeRatio);

        // 当前账户支出列表
        List<Bill> expensesBills = billMapper.selectBillList(userId, 0, null, null);
        if (CollectionUtils.isNotEmpty(expensesBills)) {
            BigDecimal expensesTotal =
                    expensesBills.stream().map(Bill::getBillNumber).reduce(BigDecimal.ZERO, BigDecimal::add);
            resultMap.put("expensesTotal", String.valueOf(expensesTotal.setScale(2, RoundingMode.HALF_UP)));
        }

        // 查询上个月支出
        BigDecimal lastMonthTotalExpenses = getMonthTotal(loginUser, false, false);
        // 查询这个月支出
        BigDecimal curMonthTotalExpenses = getMonthTotal(loginUser, true, false);
        // 计算环比值
        String expensesRatio = d2dRatio(curMonthTotalExpenses, lastMonthTotalExpenses);
        resultMap.put("expensesRatio", expensesRatio);
        result.setData(resultMap);
        putMsg(result, Status.SUCCESS);
        return result;
    }

    private BigDecimal getMonthTotal(User loginUser, boolean curMonth, boolean income) {
        String startDate;
        String endDate;
        BigDecimal returnVal = BigDecimal.ZERO;
        if (curMonth) {
            // 本月
            Instant curMonthInstant = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
            startDate =
                    DateUtils.format(DateUtils.getStartOfDay(DateUtils.getFirstDayOfMonth(Date.from(curMonthInstant))),
                            Constants.YYYY_MM_DD_HH_MM_SS);
            endDate = DateUtils.format(Date.from(curMonthInstant), Constants.YYYY_MM_DD_HH_MM_SS);
        } else {
            // 上月
            Instant lastMonthInstant = LocalDateTime.now().minusMonths(1).atZone(ZoneId.systemDefault()).toInstant();
            startDate =
                    DateUtils.format(DateUtils.getStartOfDay(DateUtils.getFirstDayOfMonth(Date.from(lastMonthInstant))),
                            Constants.YYYY_MM_DD_HH_MM_SS);
            endDate = DateUtils.format(DateUtils.getEndOfDay(DateUtils.getLastDayOfMonth(Date.from(lastMonthInstant))),
                    Constants.YYYY_MM_DD_HH_MM_SS);
        }

        Integer userId = null;
        // 如果不是管理员角色 只能查询当前用户收支明细
        if (!isAdmin(loginUser)) {
            userId = loginUser.getId();
        }

        // 查询这个月收入/支出
        Integer billType = income ? 1 : 0;
        List<Bill> selectList = billMapper.selectBillList(userId, billType, startDate, endDate);
        if (CollectionUtils.isNotEmpty(selectList)) {
            returnVal = selectList.stream().map(Bill::getBillNumber).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        return returnVal;
    }

    @Override
    public Result chartBillInYear(User loginUser, String startDate, String endDate, User user, String billType) {
        Result result = new Result();
        Map<String, Object> resultMap = new HashMap<>();
        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
            YearMonth start = YearMonth.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM"));
            YearMonth end = YearMonth.parse(endDate, DateTimeFormatter.ofPattern("yyyy-MM"));
            // 2023-08-01 00:00:00
            startDate = DateUtils.format(
                    DateUtils.getStartOfDay(
                            Date.from(start.atDay(1).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())),
                    Constants.YYYY_MM_DD_HH_MM_SS);
            // 2023-08-31 23:59:59
            endDate = DateUtils.format(
                    DateUtils.getEndOfDay(
                            Date.from(end.atEndOfMonth().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())),
                    Constants.YYYY_MM_DD_HH_MM_SS);
        }
        /*
         * QueryWrapper<Bill> queryWrapper = new QueryWrapper<>();
         * queryWrapper.select("sum(bill_number) as bill_number", "left(create_time, 7) as billNo");
         * queryWrapper.eq("bill_type", billType); // 如果不是管理员角色 只能查询当前用户收支明细 if (!isAdmin(loginUser)) {
         * queryWrapper.eq("user_id", user.getId()); } queryWrapper.between("create_time", startDate, endDate);
         * queryWrapper.groupBy("billNo").orderByAsc("billNo"); List<Bill> billList =
         * billMapper.selectList(queryWrapper);
         */
        Integer userId = null;
        // 如果不是管理员角色 只能查询当前用户收支明细
        if (!isAdmin(loginUser)) {
            userId = loginUser.getId();
        }
        List<Bill> billList = billMapper.selectBillListByMonth(userId, Integer.valueOf(billType), startDate, endDate);
        List<String> monthBetweenDate = getMonthBetweenDate(startDate, endDate);
        for (String month : monthBetweenDate) {
            resultMap.put(month, 0);
        }
        for (Bill bill : billList) {
            resultMap.put(bill.getBillNo(), bill.getBillNumber());
        }
        result.setData(resultMap);
        putMsg(result, Status.SUCCESS);
        return result;
    }

    @Override
    public void exportExcel(User loginUser, Bill bill, String startDate, String endDate, HttpServletResponse response) {
        LambdaQueryWrapper<Bill> lqw = new LambdaQueryWrapper<>();
        List<Integer> userIdList = Lists.newArrayList();
        // 如果不是管理员角色 只能查询当前用户收支明细
        // 如果用户是用户组管理员 可以查询用户组下所有用户的数据
        if (!isAdmin(loginUser)) {
            userIdList.add(loginUser.getId());
            List<Integer> userIds = userGroupService.queryAllUserByGroupAdmin(loginUser);
            if(CollectionUtils.isNotEmpty(userIds)){
                userIdList.addAll(userIds);
            }
        }

        // 交易账户
        if (Objects.nonNull(bill.getUserId())) {
            lqw.eq(Bill::getUserId, bill.getUserId());
        } else {
            if (CollectionUtils.isNotEmpty(userIdList)) {
                lqw.in(Bill::getUserId, userIdList);
            }
        }
        // 收支类型
        if (Objects.nonNull(bill.getBillType())) {
            lqw.eq(Bill::getBillType, bill.getBillType());
        }
        // 交易类型
        if (Objects.nonNull(bill.getTradeType())) {
            lqw.eq(Bill::getTradeType, bill.getTradeType());
        }
        // 开始时间
        if (StringUtils.isNotBlank(startDate)) {
            // LocalDate ld = LocalDate.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            // Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            // Date start = Date.from(instant);
            // lqw.ge(Bill::getCreateTime, DateUtils.format(DateUtils.getStartOfDay(start),
            // Constants.YYYY_MM_DD_HH_MM_SS));
            lqw.ge(Bill::getCreateTime, startDate);
        }
        // 结束时间
        if (StringUtils.isNotBlank(endDate)) {
            // LocalDate ld = LocalDate.parse(endDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            // Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            // Date end = Date.from(instant);
            // lqw.lt(Bill::getCreateTime, DateUtils.format(DateUtils.getEndOfDay(end), Constants.YYYY_MM_DD_HH_MM_SS));
            lqw.lt(Bill::getCreateTime, endDate);
        }
        lqw.orderByDesc(Bill::getCreateTime);
        List<Bill> billList = billMapper.selectList(lqw);
        List<User> userList = userMapper.selectList(null);
        Map<Integer, String> userMap = new HashMap<Integer, String>();
        if (CollectionUtils.isNotEmpty(userList)) {
            userMap = userList.stream().collect(Collectors.toMap(User::getId, User::getUserName));
        }
        for (Bill bill1 : billList) {
            bill1.setBillNumber(bill1.getBillNumber().setScale(2, RoundingMode.HALF_UP));
            bill1.setUserName(
                    StringUtils.isNotBlank(userMap.get(bill1.getUserId())) ? userMap.get(bill1.getUserId()) : null);
        }
        List<BillExportVo> exportVoList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(billList)) {
            exportVoList = billList.stream().map(it -> {
                BillExportVo billExportVo = new BillExportVo();
                try {
                    BeanUtils.copyProperties(billExportVo, it);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return billExportVo;
            }).collect(Collectors.toList());
        }

        if (loginUser.getUserType() != UserType.ADMIN_USER) {
            ExcelUtil.exportExcel(exportVoList, "收支明细", BillExportVo.class, response);
        }else{
            ExcelUtil.exportExcelExcludeColumn(exportVoList, "收支明细", BillExportVo.class, response,Arrays.asList("balance"));
        }
    }

    /**
     * 求环比,计算公式=(本期数据-上期数据)/上期数据 *100%
     * @param bCount 本期数据
     * @param sCount 上期数据
     * @return
     */
    public String d2dRatio(BigDecimal bCount, BigDecimal sCount) {
        // 差
        BigDecimal differ = bCount.subtract(sCount);
        // 除数
        BigDecimal denominator = differ;
        // 被除数
        BigDecimal numerator = sCount;
        // 百分比
        String ratio;
        if (sCount.compareTo(BigDecimal.ZERO) == 0) {
            // 被除数为零，无意义
            ratio = new DecimalFormat("#########0.00%").format(sCount);
        } else {
            // 商
            BigDecimal quotient = denominator.divide(numerator, 6, BigDecimal.ROUND_HALF_DOWN);
            ratio = new DecimalFormat("#########0.00%").format(quotient);
        }
        return ratio;
    }

    /**
     * 获取两个日期之间的所有月份 (年月)
     *
     * @param startTime
     * @param endTime
     * @return：list
     */
    public List<String> getMonthBetweenDate(String startTime, String endTime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        // 声明保存日期集合
        List<String> list = new ArrayList<>();
        try {
            // 转化成日期类型
            Date startDate = sdf.parse(startTime);
            Date endDate = sdf.parse(endTime);

            // 用Calendar 进行日期比较判断
            Calendar calendar = Calendar.getInstance();
            while (startDate.getTime() <= endDate.getTime()) {

                // 把日期添加到集合
                list.add(sdf.format(startDate));

                // 设置日期
                calendar.setTime(startDate);

                // 把月数增加 1
                calendar.add(Calendar.MONTH, 1);

                // 获取增加后的日期
                startDate = calendar.getTime();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    public void saveBill(Bill bill) {
    	billMapper.insert(bill);
    }
}
